package pl.edu.mimuw.irs.core.model.design.processing;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import pl.edu.mimuw.irs.core.util.NamingTool;

/**
 * @author Jakub Rauch
 * Created on: 2009-05-07
 */
public class Requirement implements Serializable {

	private static final long serialVersionUID = 8430054870597839096L;
	
	// *** Attributes

	// Set of custom roles that an actor must be capable of acting to meet this requirement.
	private Map<Role, Integer> rolesMap;
	private List<RoleInstance> rolesInstances;
	private String name;
	private boolean nameGenerated;
	public Map<Role, Integer> getRolesMap() {
		if (this.rolesMap == null) this.rolesMap = new HashMap<Role, Integer>();
		return Collections.unmodifiableMap(this.rolesMap);
	}
	public List<RoleInstance> getRolesInstances() {
		if (rolesInstances == null) rolesInstances = new ArrayList<RoleInstance>();
		return rolesInstances;
	}
	public void setRoles(Collection<RoleInstance> roles) {
		getRolesMap().clear();
		getRolesInstances().clear();
		for (RoleInstance role : roles) {
			Integer count = rolesMap.get(role.getRole());
			if (count == null) {
				count = 0;
			}
			rolesMap.put(role.getRole(), count + 1);
			rolesInstances.add(role);
		}
	}
	public void addRoleInstance(RoleInstance roleInstance) {
		Integer count = getRolesMap().get(roleInstance.getRole());
		if (count == null) count = 0;
		rolesMap.put(roleInstance.getRole(), count + 1);
		getRolesInstances().add(roleInstance);
	}
	public void removeRoleInstance(RoleInstance roleInstance) {
		Integer count = getRolesMap().get(roleInstance.getRole());
		if (count != null) {
			if (count > 1) {
				rolesMap.put(roleInstance.getRole(), count - 1);
			} else {
				rolesMap.remove(roleInstance.getRole());
			}
		}
		getRolesInstances().remove(roleInstance);
	}
	public String getName() {
		/*if (name == null) XXX : poprawić nazwę requirement*/  name = createNewName();
		if (nameGenerated) name = createNewName();
		return name;
	}
	public void setName(String name) {
		this.name = name;
		if (this.name != null && nameGenerated) nameGenerated = false;
	}
	
	// *** Constructors

	public Requirement(Collection<RoleInstance> roleInstances) {
		for (RoleInstance role : roleInstances) {
			addRoleInstance(role);
		}
	}
	
	public Requirement(RoleInstance... roleInstances) {
		for (RoleInstance role : roleInstances) {
			addRoleInstance(role);
		}
	}
	
	public Requirement(String name) {
		setName(name);
	}
	
	public Requirement() {
		this((String)null);
	}
	
	// *** Methods
	
	private String createNewName() {
		StringBuffer nameBuffer = new StringBuffer();
		nameBuffer.append("Req");
		if (getRolesInstances().size() > 0) {
			nameBuffer.append("[");
			for (RoleInstance instance : getRolesInstances()) {
				nameBuffer.append(NamingTool.abbreviation(instance.toString()));
				nameBuffer.append(";");
			}
			nameBuffer.replace(nameBuffer.lastIndexOf(";"), 
					nameBuffer.length(), "]");
		} else {
			nameBuffer.append("[]");
		}
		return nameBuffer.toString();
	}
	
	@Override
	public String toString() {
		return getName();
	}
	
	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Requirement) {
			Requirement requirement = (Requirement) obj;
			Map<Role, Integer> roles = requirement.getRolesMap();
			Map<Role, Integer> thisRoles = getRolesMap();
			if (roles.size() == thisRoles.size()) {
				for (Entry<Role, Integer> role : roles.entrySet()) {
					if (thisRoles.get(role.getKey()) != role.getValue()) {
						return false;
					}
				}
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}
	
	@Override
	public int hashCode() {
		int sum = 0;
		for (Entry<Role, Integer> role : getRolesMap().entrySet()) {
			sum += role.getKey().hashCode();
		}
		return sum;
	}
}
